#include <math.h>
#include <sys/time.h>

#import <CoreGraphics/CGGeometry.h>

/*
	(x, y)の二次元座標系での2点間の距離を返す
*/
double diffLength(const CGPoint *a, const CGPoint *b)
{
	return sqrt(pow(a->x - b->x, 2.0) + pow(a->y - b->y, 2.0));
}

/*
	http://kzk9.net/column/time.html
*/
double gettimeofday_sec()
{
	struct timeval t;
	gettimeofday(&t, NULL);
	return t.tv_sec + (t.tv_usec / 1000.0 / 1000.0);
}
#include <limits.h>

#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSTimer.h>
#import <UIKit/UIHardware.h>

#import "MousePanel.h"

@implementation MousePanel

- (id)initWithFrame: (CGRect)frame
{
	self = [super initWithFrame: frame];
	
	mouseDelegate = nil;
	prevPoint = prevP1 = prevP2 = CGPointZero;
	mouseMoved = leftDragging = rightDragging = NO;
	leftClickTimer = rightClickTimer = nil;
	mouseUpTime = gettimeofday_sec();
	
	return self;
}

- (void)dealloc
{
	[super dealloc];
}

- (void)setMouseDelegate: (id)delegate
{
	mouseDelegate = delegate;
}

- (BOOL)canHandleGestures
{
	return YES;
}

- (void)gestureStarted: (GSEventRef)event
{
	gesStart1 = GSEventGetInnerMostPathPosition(event);
	gesStart2 = GSEventGetInnerMostPathPosition(event);
	gesStartTime = gettimeofday_sec();
	
	// クリック発生前ならドラッグ開始
	if (rightClickTimer && [rightClickTimer isValid])
	{
		[rightClickTimer invalidate];
		
		[mouseDelegate mouseRightButtonPress];
		rightDragging = YES;
	}
	
	prevP1 = gesStart1;
	prevP2 = gesStart2;
}

- (void)gestureEnded: (GSEventRef)event
{
	gesEnd1 = GSEventGetInnerMostPathPosition(event);
	gesEnd2 = GSEventGetInnerMostPathPosition(event);
	
	// 右ドラッグ中ならリリース
	if (rightDragging)
	{
		[mouseDelegate mouseRightButtonRelease];
		rightDragging = NO;
		return;
	}
	
	if (diffLength(&gesStart1, &gesEnd1) < SAME_POINT_MARGIN && diffLength(&gesStart2, &gesEnd2) < SAME_POINT_MARGIN && (gettimeofday_sec() - gesStartTime) < (CLICK_INTERVAL / 1000.0))
	{
		[rightClickTimer release];
		rightClickTimer = [NSTimer scheduledTimerWithTimeInterval: (CLICK_TIMEOUT / 1000.0) target: self selector: @selector(sendMouseRightButtonClick:) userInfo: nil repeats: NO];
		[rightClickTimer retain];
	}
}

- (void)gestureChanged: (GSEventRef)event
{
	CGSize screen = [UIHardware fullScreenApplicationContentRect].size;
	
	CGPoint p1 = GSEventGetInnerMostPathPosition(event);
	CGPoint p2 = GSEventGetInnerMostPathPosition(event);
	
	int x = (int)(p1.x / screen.width * USHRT_MAX);
	int y = (int)(p1.y / screen.height * USHRT_MAX);
	
	if (rightDragging)
	{
		[mouseDelegate mouseMoveAbsolute: CGPointMake(x, y)];
		[mouseDelegate mouseMoveRelative: CGPointMake(p1.x - prevP1.x, p1.y - prevP1.y)];
	}
	else	// scroll
	{
		// 1でも2でも同じ動作?
		if (1 < (p1.y - prevP1.y) || 1 < (prevP1.x - p1.x))
			[mouseDelegate mouseWheel: -1];
		else if (1 < (prevP1.y - p1.y) || 1 < (p1.x - prevP1.x))
			[mouseDelegate mouseWheel: 1];
	}
	
	prevP1 = p1;
	prevP2 = p2;
}

- (void)mouseDragged: (GSEventRef)event
{
	CGSize screen = [UIHardware fullScreenApplicationContentRect].size;
	CGPoint p = GSEventGetLocationInWindow(event).origin;
	
/*
	int margin = [[NSUserDefaults standardUserDefaults] integerForKey: @"margin"];
*/
	
	int x = (int)(p.x / screen.width * USHRT_MAX);
	int y = (int)(p.y / screen.height * USHRT_MAX);
	
	[mouseDelegate mouseMoveAbsolute: CGPointMake(x, y)];
	[mouseDelegate mouseMoveRelative: CGPointMake(p.x - prevPoint.x, p.y - prevPoint.y)];
	
	prevPoint = p;
}

- (void)mouseDown: (GSEventRef)event
{
	CGPoint p = GSEventGetLocationInWindow(event).origin;
	
	// クリックが発生前ならば、左ドラッグ開始
	if (leftClickTimer && [leftClickTimer isValid])
	{
		[leftClickTimer invalidate];
		
		[mouseDelegate mouseLeftButtonPress];
		leftDragging = YES;
	}
	
	mouseDownPoint = prevPoint = p;
	mouseDownTime = gettimeofday_sec();
}

- (void)mouseUp: (GSEventRef)event
{
	CGPoint p = GSEventGetLocationInWindow(event).origin;
	
	double prevMouseUpTime = mouseUpTime;
	mouseUpTime = gettimeofday_sec();
	
	// double click
	if (!mouseMoved && (gettimeofday_sec() - prevMouseUpTime) < (CLICK_TIMEOUT / 1000.0) && diffLength(&p, &mouseDownPoint) < SAME_POINT_MARGIN)
	{
		if (!leftDragging)
			[mouseDelegate mouseLeftButtonPress];
		[mouseDelegate mouseLeftButtonRelease];
		leftDragging = NO;
		[mouseDelegate mouseLeftButtonClick];
		return;
	}
	
	// 今回のマウスイベントがカーソル移動だったらYES
	mouseMoved = ((SAME_POINT_MARGIN < diffLength(&p, &mouseDownPoint)) ? YES : NO);
	
	// 左ドラッグ中だった場合、ドラッグを終了
	if (leftDragging)
	{
		[mouseDelegate mouseLeftButtonRelease];
		leftDragging = NO;
		return;
	}
	
	// single click
	if (diffLength(&p, &mouseDownPoint) < SAME_POINT_MARGIN && (gettimeofday_sec() - mouseDownTime) < (CLICK_INTERVAL / 1000.0))
	{
		[leftClickTimer release];
		leftClickTimer = [NSTimer scheduledTimerWithTimeInterval: (CLICK_TIMEOUT / 1000.0) target: self selector: @selector(sendMouseLeftButtonClick:) userInfo: nil repeats: NO];
		[leftClickTimer retain];
	}
}

- (void)sendMouseLeftButtonClick: (NSTimer *)timer
{
	[mouseDelegate mouseLeftButtonClick];
}

- (void)sendMouseRightButtonClick: (NSTimer *)timer
{
	[mouseDelegate mouseRightButtonClick];
}

@end
